<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport"
          content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover">
    <meta name="format-detection" content="telephone=no">
    <style type="text/css">

#watermark {

  position: relative;
  overflow: hidden;
}

#watermark .x {
  position: absolute;
  top: 800;
  left: 400;
  color: #3300ff;
  font-size: 50px;
  pointer-events: none;
  opacity:0.3;
  filter:Alpha(opacity=50);
  
  
}
    </style>


    <style type="text/css">
 html{color:#333;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;text-rendering:optimizelegibility;font-family:Helvetica Neue,PingFang SC,Verdana,Microsoft Yahei,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif}html.borderbox *,html.borderbox :after,html.borderbox :before{box-sizing:border-box}article,aside,blockquote,body,button,code,dd,details,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hr,input,legend,li,menu,nav,ol,p,pre,section,td,textarea,th,ul{margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,menu,nav,section{display:block}audio,canvas,video{display:inline-block}body,button,input,select,textarea{font:300 1em/1.8 PingFang SC,Lantinghei SC,Microsoft Yahei,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,Helvetica,sans-serif}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}blockquote{position:relative;color:#999;font-weight:400;border-left:1px solid #1abc9c;padding-left:1em;margin:1em 3em 1em 2em}@media only screen and (max-width:640px){blockquote{margin:1em 0}}abbr,acronym{border-bottom:1px dotted;font-variant:normal}abbr{cursor:help}del{text-decoration:line-through}address,caption,cite,code,dfn,em,th,var{font-style:normal;font-weight:400}ol,ul{list-style:none}caption,th{text-align:left}q:after,q:before{content:""}sub,sup{font-size:75%;line-height:0;position:relative}:root sub,:root sup{vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}a{color:#1abc9c}a:hover{text-decoration:underline}.typo a{border-bottom:1px solid #1abc9c}.typo a:hover{border-bottom-color:#555;color:#555}.typo a:hover,a,ins{text-decoration:none}.typo-u,u{text-decoration:underline}mark{background:#fffdd1;border-bottom:1px solid #ffedce;padding:2px;margin:0 5px}code,pre,pre tt{font-family:Courier,Courier New,monospace}pre{background:hsla(0,0%,97%,.7);border:1px solid #ddd;padding:1em 1.5em;display:block;-webkit-overflow-scrolling:touch}hr{border:none;border-bottom:1px solid #cfcfcf;margin-bottom:.8em;height:10px}.typo-small,figcaption,small{font-size:.9em;color:#888}b,strong{font-weight:700;color:#000}[draggable]{cursor:move}.clearfix:after,.clearfix:before{content:"";display:table}.clearfix:after{clear:both}.clearfix{zoom:1}.textwrap,.textwrap td,.textwrap th{word-wrap:break-word;word-break:break-all}.textwrap-table{table-layout:fixed}.serif{font-family:Palatino,Optima,Georgia,serif}.typo-dl,.typo-form,.typo-hr,.typo-ol,.typo-p,.typo-pre,.typo-table,.typo-ul,.typo dl,.typo form,.typo hr,.typo ol,.typo p,.typo pre,.typo table,.typo ul,blockquote{margin-bottom:1rem}h1,h2,h3,h4,h5,h6{font-family:PingFang SC,Helvetica Neue,Verdana,Microsoft Yahei,Hiragino Sans GB,Microsoft Sans Serif,WenQuanYi Micro Hei,sans-serif;color:#000;line-height:1.35}.typo-h1,.typo-h2,.typo-h3,.typo-h4,.typo-h5,.typo-h6,.typo h1,.typo h2,.typo h3,.typo h4,.typo h5,.typo h6{margin-top:1.2em;margin-bottom:.6em;line-height:1.35}.typo-h1,.typo h1{font-size:2em}.typo-h2,.typo h2{font-size:1.8em}.typo-h3,.typo h3{font-size:1.6em}.typo-h4,.typo h4{font-size:1.4em}.typo-h5,.typo-h6,.typo h5,.typo h6{font-size:1.2em}.typo-ul,.typo ul{margin-left:1.3em;list-style:disc}.typo-ol,.typo ol{list-style:decimal;margin-left:1.9em}.typo-ol ol,.typo-ol ul,.typo-ul ol,.typo-ul ul,.typo li ol,.typo li ul{margin-bottom:.8em;margin-left:2em}.typo-ol ul,.typo-ul ul,.typo li ul{list-style:circle}.typo-table td,.typo-table th,.typo table caption,.typo table td,.typo table th{border:1px solid #ddd;padding:.5em 1em;color:#666}.typo-table th,.typo table th{background:#fbfbfb}.typo-table thead th,.typo table thead th{background:hsla(0,0%,95%,.7)}.typo table caption{border-bottom:none}.typo-input,.typo-textarea{-webkit-appearance:none;border-radius:0}.typo-em,.typo em,caption,legend{color:#000;font-weight:inherit}.typo-em{position:relative}.typo-em:after{position:absolute;top:.65em;left:0;width:100%;overflow:hidden;white-space:nowrap;content:"\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB\30FB"}.typo img{max-width:100%}.common-content{font-weight:400;color:#353535;line-height:1.75rem;white-space:normal;word-break:normal;font-size:1rem}.common-content img{display:block;max-width:100%;background-color:#eee}.common-content audio,.common-content video{width:100%;background-color:#eee}.common-content center,.common-content font{margin-top:1rem;display:inline-block}.common-content center{width:100%}.common-content pre{margin-top:1rem;padding-left:0;padding-right:0;position:relative;overflow:hidden}.common-content pre code{font-size:.8rem;font-family:Consolas,Liberation Mono,Menlo,monospace,Courier;display:block;width:100%;box-sizing:border-box;padding-left:1rem;padding-right:1rem;overflow-x:auto}.common-content hr{border:none;margin-top:1.5rem;margin-bottom:1.5rem;border-top:1px solid #f5f5f5;height:1px;background:none}.common-content b,.common-content h1,.common-content h2,.common-content h3,.common-content h4,.common-content h5,.common-content strong{font-weight:700}.common-content h1,.common-content h2{font-size:1.125rem;margin-bottom:.45rem}.common-content h3,.common-content h4,.common-content h5{font-size:1rem;margin-bottom:.45rem}.common-content p{font-weight:400;color:#353535;margin-top:.15rem}.common-content .orange{color:#ff5a05}.common-content .reference{font-size:1rem;color:#888}.custom-rich-content h1{margin-top:0;font-weight:400;font-size:15.25px;border-bottom:1px solid #eee;line-height:2.8}.custom-rich-content li,.custom-rich-content p{font-size:14px;color:#888;line-height:1.6}table.hljs-ln{margin-bottom:0;border-spacing:0;border-collapse:collapse}table.hljs-ln,table.hljs-ln tbody,table.hljs-ln td,table.hljs-ln tr{box-sizing:border-box}table.hljs-ln td{padding:0;border:0}table.hljs-ln td.hljs-ln-numbers{min-width:15px;color:rgba(27,31,35,.3);text-align:right;white-space:nowrap;cursor:pointer;user-select:none}table.hljs-ln td.hljs-ln-code,table.hljs-ln td.hljs-ln-numbers{font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;font-size:12px;line-height:20px;vertical-align:top}table.hljs-ln td.hljs-ln-code{position:relative;padding-right:10px;padding-left:10px;overflow:visible;color:#24292e;word-wrap:normal;white-space:pre}video::-webkit-media-controls{overflow:hidden!important}video::-webkit-media-controls-enclosure{width:calc(100% + 32px);margin-left:auto}.button-cancel{color:#888;border:1px solid #888;border-radius:3px;margin-right:12px}.button-cancel,.button-primary{-ms-flex-positive:1;flex-grow:1;height:35px;display:inline-block;font-size:15px;text-align:center;line-height:36px}.button-primary{color:#fff;background-color:#ff5a05;border-radius:3px}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/font_372689_bwwwtosxtzp.eot);src:url(//at.alicdn.com/t/font_372689_bwwwtosxtzp.eot#iefix) format("embedded-opentype"),url(//at.alicdn.com/t/font_372689_bwwwtosxtzp.woff) format("woff"),url(//at.alicdn.com/t/font_372689_bwwwtosxtzp.ttf) format("truetype"),url(//at.alicdn.com/t/font_372689_bwwwtosxtzp.svg#iconfont) format("svg")}@font-face{font-family:player-font;src:url(//at.alicdn.com/t/font_509397_1cyjv4o90qiod2t9.eot);src:url(//at.alicdn.com/t/font_509397_1cyjv4o90qiod2t9.eot#iefix) format("embedded-opentype"),url(//at.alicdn.com/t/font_509397_1cyjv4o90qiod2t9.woff) format("woff"),url(//at.alicdn.com/t/font_509397_1cyjv4o90qiod2t9.ttf) format("truetype"),url(//at.alicdn.com/t/font_509397_1cyjv4o90qiod2t9.svg#player-font) format("svg")}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal;-webkit-font-smoothing:antialiased;-webkit-text-stroke-width:.2px;-moz-osx-font-smoothing:grayscale}html{background:#fff;min-height:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{width:100%}body.fixed{overflow:hidden;position:fixed;width:100vw;height:100vh}i{font-style:normal}a{word-wrap:break-word;-webkit-tap-highlight-color:rgba(0,0,0,0)}a:hover{text-decoration:none}.fade-enter-active,.fade-leave-active{transition:opacity .3s}.fade-enter,.fade-leave-to{opacity:0}.MathJax,.MathJax_CHTML,.MathJax_MathContainer,.MathJax_MathML,.MathJax_PHTML,.MathJax_PlainSource,.MathJax_SVG{outline:0}.ios-app-switch .js-audit{display:none}._loading_wrap_{position:fixed;width:100vw;height:100vh;top:50%;left:50%;transform:translate(-50%,-50%);z-index:999}._loading_div_class_,._loading_wrap_{display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}._loading_div_class_{word-wrap:break-word;padding:.5rem .75rem;text-align:center;z-index:9999;font-size:.6rem;max-width:60%;color:#fff;border-radius:.25rem;-ms-flex-direction:column;flex-direction:column}._loading_div_class_ .message{color:#353535;font-size:16px;line-height:3}.spinner{animation:circle-rotator 1.4s linear infinite}.spinner *{line-height:0;box-sizing:border-box}@keyframes circle-rotator{0%{transform:rotate(0deg)}to{transform:rotate(270deg)}}.path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;animation:circle-dash 1.4s ease-in-out infinite,circle-colors 5.6s ease-in-out infinite}@keyframes circle-colors{0%{stroke:#ff5a05}to{stroke:#ff5a05}}@keyframes circle-dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}to{stroke-dashoffset:187;transform:rotate(450deg)}}.confirm-box-wrapper,.confirm-box-wrapper .mask{position:absolute;top:0;left:0;right:0;bottom:0}.confirm-box-wrapper .mask{background:rgba(0,0,0,.6)}.confirm-box-wrapper .confirm-box{position:fixed;top:50%;left:50%;width:267px;background:#fff;transform:translate(-50%,-50%);border-radius:7px}.confirm-box-wrapper .confirm-box .head{margin:0 18px;font-size:18px;text-align:center;line-height:65px;border-bottom:1px solid #d9d9d9}.confirm-box-wrapper .confirm-box .body{padding:18px;padding-bottom:0;color:#353535;font-size:12.5px;max-height:150px;overflow:auto}.confirm-box-wrapper .confirm-box .foot{display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;padding:18px}.confirm-box-wrapper .confirm-box .foot .button-cancel{border:1px solid #d9d9d9}.hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}




    </style>
    <style type="text/css">
        .button-cancel[data-v-87ffcada]{color:#888;border:1px solid #888;border-radius:3px;margin-right:12px}.button-cancel[data-v-87ffcada],.button-primary[data-v-87ffcada]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;height:35px;display:inline-block;font-size:15px;text-align:center;line-height:36px}.button-primary[data-v-87ffcada]{color:#fff;background-color:#ff5a05;border-radius:3px}.pd[data-v-87ffcada]{padding-left:1.375rem;padding-right:1.375rem}.article[data-v-87ffcada]{max-width:70rem;margin:0 auto}.article .article-unavailable[data-v-87ffcada]{color:#fa8919;font-size:15px;font-weight:600;line-height:24px;border-radius:5px;padding:12px;background-color:#f6f7fb;margin-top:20px}.article .article-unavailable .iconfont[data-v-87ffcada]{font-size:12px}.article .main[data-v-87ffcada]{padding:1.25rem 0;margin-bottom:52px}.article-title[data-v-87ffcada]{color:#353535;font-weight:400;line-height:1.65rem;font-size:1.34375rem}.article-info[data-v-87ffcada]{color:#888;font-size:.9375rem;margin-top:1.0625rem}.article-content[data-v-87ffcada]{margin-top:1.0625rem}.article-content.android video[data-v-87ffcada]::-webkit-media-controls-fullscreen-button{display:none}.copyright[data-v-87ffcada]{color:#b2b2b2;padding-bottom:20px;margin-top:20px;font-size:13px}.audio-player[data-v-87ffcada]{width:100%;margin:20px 0}.to-comment[data-v-87ffcada]{overflow:hidden;padding-top:10px;margin-bottom:-30px}.to-comment a.button-primary[data-v-87ffcada]{float:right;height:20px;font-size:12px;line-height:20px;padding:4px 8px;cursor:pointer}.article-comments[data-v-87ffcada]{margin-top:2rem}.article-comments h2[data-v-87ffcada]{text-align:center;color:#888;position:relative;z-index:1;margin-bottom:1rem}.article-comments h2[data-v-87ffcada]:before{border-top:1px dotted #888;content:"";position:absolute;top:56%;left:0;width:100%;z-index:-1}.article-comments h2 span[data-v-87ffcada]{font-size:15.25px;font-weight:400;padding:0 1rem;background:#fff;display:inline-block}.article-sub-bottom[data-v-87ffcada]{z-index:10;cursor:pointer}.switch-btns[data-v-87ffcada]{height:76px;cursor:pointer;padding-top:24px;padding-bottom:24px;border-bottom:10px solid #f6f7fb;position:relative}.switch-btns[data-v-87ffcada]:before{content:" ";height:1px;background:#e8e8e8;position:absolute;top:0;left:0;-webkit-box-sizing:border-box;box-sizing:border-box;left:1.375rem;right:1.375rem}.switch-btns .btn[data-v-87ffcada]{height:38px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.switch-btns .btn .tag[data-v-87ffcada]{-webkit-box-flex:0;-ms-flex:0 0 62px;flex:0 0 62px;text-align:center;color:#888;font-size:14px;border-radius:10px;height:22px;line-height:22px;background:#f6f7fb;font-weight:400}.switch-btns .btn .txt[data-v-87ffcada]{margin-left:10px;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;color:#888;font-size:15px;height:22px;line-height:22px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400}@media (max-width:769px){.article .breadcrumb[data-v-87ffcada]{padding-top:10px;padding-bottom:10px}}





    </style>

    <style type="text/css">
        .comment-item{list-style-position:inside;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;margin-bottom:1rem}.comment-item a{border-bottom:none}.comment-item .avatar{width:2.625rem;height:2.625rem;-ms-flex-negative:0;flex-shrink:0;border-radius:50%}.comment-item .info{margin-left:.5rem;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.comment-item .info .hd{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.comment-item .info .hd .username{color:#888;font-size:15.25px;font-weight:400;line-height:1.2}.comment-item .info .hd .control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.comment-item .info .hd .control .btn-share{color:#888;font-size:.75rem;margin-right:1rem}.comment-item .info .hd .control .btn-praise{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:15.25px;text-decoration:none}.comment-item .info .hd .control .btn-praise i{color:#888;display:inline-block;font-size:.75rem;margin-right:.3rem;margin-top:-.01rem}.comment-item .info .hd .control .btn-praise i.on,.comment-item .info .hd .control .btn-praise span{color:#ff5a05}.comment-item .info .bd{color:#353535;font-size:15.25px;font-weight:400;white-space:normal;word-break:break-all;line-height:1.6}.comment-item .info .time{color:#888;font-size:9px;line-height:1}.comment-item .info .reply .reply-hd{font-size:15.25px}.comment-item .info .reply .reply-hd span{margin-left:-12px;color:#888;font-weight:400}.comment-item .info .reply .reply-hd i{color:#ff5a05;font-size:15.25px}.comment-item .info .reply .reply-content{color:#353535;font-size:15.25px;font-weight:400;white-space:normal;word-break:break-all}.comment-item .info .reply .reply-time{color:#888;font-size:9px}




    </style>
</head>
<body>
<div id="app">


    <div data-v-87ffcada="" class="article" id="watermark">
        <p class="x">加微信heibaifk，网盘停止更新</p>
        <div data-v-87ffcada="" class="main main-app">
            <h1 data-v-87ffcada="" class="article-title pd">
                13讲为什么表数据删掉一半，表文件大小不变
            </h1>
            <div data-v-87ffcada="" class="article-content typo common-content pd"><img data-v-87ffcada=""
                                                                                        src="https://static001.geekbang.org/resource/image/7e/11/7e411ea05dde5a1bf3a4106fb8e5ae11.jpg">


                <div data-v-87ffcada="" id="article-content" class="">
                    <div class="text">
                        <p>经常会有同学来问我，我的数据库占用空间太大，我把一个最大的表删掉了一半的数据，怎么表文件的大小还是没变？</p><p>那么今天，我就和你聊聊数据库表的空间回收，看看如何解决这个问题。</p><p>这里，我们还是针对MySQL中应用最广泛的InnoDB引擎展开讨论。一个InnoDB表包含两部分，即：表结构定义和数据。在MySQL 8.0版本以前，表结构是存在以.frm为后缀的文件里。而MySQL 8.0版本，则已经允许把表结构定义放在系统数据表中了。因为表结构定义占用的空间很小，所以我们今天主要讨论的是表数据。</p><p>接下来，我会先和你说明为什么简单地删除表数据达不到表空间回收的效果，然后再和你介绍正确回收空间的方法。</p><h1>参数innodb_file_per_table</h1><p>表数据既可以存在共享表空间里，也可以是单独的文件。这个行为是由参数innodb_file_per_table控制的：</p><ol>
<li>
<p>这个参数设置为OFF表示的是，表的数据放在系统共享表空间，也就是跟数据字典放在一起；</p>
</li>
<li>
<p>这个参数设置为ON表示的是，每个InnoDB表数据存储在一个以 .ibd为后缀的文件中。</p>
</li>
</ol><p>从MySQL 5.6.6版本开始，它的默认值就是ON了。</p><p>我建议你不论使用MySQL的哪个版本，都将这个值设置为ON。因为，一个表单独存储为一个文件更容易管理，而且在你不需要这个表的时候，通过drop table命令，系统就会直接删除这个文件。而如果是放在共享表空间中，即使表删掉了，空间也是不会回收的。</p><!-- [[[read_end]]] --><p>所以，<strong>将innodb_file_per_table设置为ON，是推荐做法，我们接下来的讨论都是基于这个设置展开的。</strong></p><p>我们在删除整个表的时候，可以使用drop table命令回收表空间。但是，我们遇到的更多的删除数据的场景是删除某些行，这时就遇到了我们文章开头的问题：表中的数据被删除了，但是表空间却没有被回收。</p><p>我们要彻底搞明白这个问题的话，就要从数据删除流程说起了。</p><h1>数据删除流程</h1><p>我们先再来看一下InnoDB中一个索引的示意图。在前面<a href="https://time.geekbang.org/column/article/69236">第4</a>和<a href="https://time.geekbang.org/column/article/69636">第5</a>篇文章中，我和你介绍索引时曾经提到过，InnoDB里的数据都是用B+树的结构组织的。</p><p><img src="https://static001.geekbang.org/resource/image/f0/c8/f0b1e4ac610bcb5c5922d0b18563f3c8.png" alt=""></p><center><span class="reference">图1 B+树索引示意图</span></center><p>假设，我们要删掉R4这个记录，InnoDB引擎只会把R4这个记录标记为删除。如果之后要再插入一个ID在300和600之间的记录时，可能会复用这个位置。但是，磁盘文件的大小并不会缩小。</p><p>现在，你已经知道了InnoDB的数据是按页存储的，那么如果我们删掉了一个数据页上的所有记录，会怎么样？</p><p>答案是，整个数据页就可以被复用了。</p><p>但是，<strong>数据页的复用跟记录的复用是不同的。</strong></p><p>记录的复用，只限于符合范围条件的数据。比如上面的这个例子，R4这条记录被删除后，如果插入一个ID是400的行，可以直接复用这个空间。但如果插入的是一个ID是800的行，就不能复用这个位置了。</p><p>而当整个页从B+树里面摘掉以后，可以复用到任何位置。以图1为例，如果将数据页page A上的所有记录删除以后，page A会被标记为可复用。这时候如果要插入一条ID=50的记录需要使用新页的时候，page A是可以被复用的。</p><p>如果相邻的两个数据页利用率都很小，系统就会把这两个页上的数据合到其中一个页上，另外一个数据页就被标记为可复用。</p><p>进一步地，如果我们用delete命令把整个表的数据删除呢？结果就是，所有的数据页都会被标记为可复用。但是磁盘上，文件不会变小。</p><p>你现在知道了，delete命令其实只是把记录的位置，或者数据页标记为了“可复用”，但磁盘文件的大小是不会变的。也就是说，通过delete命令是不能回收表空间的。这些可以复用，而没有被使用的空间，看起来就像是“空洞”。</p><p>实际上，<strong>不止是删除数据会造成空洞，插入数据也会。</strong></p><p>如果数据是按照索引递增顺序插入的，那么索引是紧凑的。但如果数据是随机插入的，就可能造成索引的数据页分裂。</p><p>假设图1中page A已经满了，这时我要再插入一行数据，会怎样呢？</p><p><img src="https://static001.geekbang.org/resource/image/80/ea/8083f05a4a4c0372833a6e01d5a8e6ea.png" alt=""></p><center><span class="reference">图2 插入数据导致页分裂</span></center><p>可以看到，由于page A满了，再插入一个ID是550的数据时，就不得不再申请一个新的页面page B来保存数据了。页分裂完成后，page A的末尾就留下了空洞（注意：实际上，可能不止1个记录的位置是空洞）。</p><p>另外，更新索引上的值，可以理解为删除一个旧的值，再插入一个新值。不难理解，这也是会造成空洞的。</p><p>也就是说，经过大量增删改的表，都是可能是存在空洞的。所以，如果能够把这些空洞去掉，就能达到收缩表空间的目的。</p><p>而重建表，就可以达到这样的目的。</p><h1>重建表</h1><p>试想一下，如果你现在有一个表A，需要做空间收缩，为了把表中存在的空洞去掉，你可以怎么做呢？</p><p>你可以新建一个与表A结构相同的表B，然后按照主键ID递增的顺序，把数据一行一行地从表A里读出来再插入到表B中。</p><p>由于表B是新建的表，所以表A主键索引上的空洞，在表B中就都不存在了。显然地，表B的主键索引更紧凑，数据页的利用率也更高。如果我们把表B作为临时表，数据从表A导入表B的操作完成后，用表B替换A，从效果上看，就起到了收缩表A空间的作用。</p><p>这里，你可以使用alter table A engine=InnoDB命令来重建表。在MySQL 5.5版本之前，这个命令的执行流程跟我们前面描述的差不多，区别只是这个临时表B不需要你自己创建，MySQL会自动完成转存数据、交换表名、删除旧表的操作。</p><p><img src="https://static001.geekbang.org/resource/image/02/cd/02e083adaec6e1191f54992f7bc13dcd.png" alt=""></p><center><span class="reference">图3 改锁表DDL</span></center><p>显然，花时间最多的步骤是往临时表插入数据的过程，如果在这个过程中，有新的数据要写入到表A的话，就会造成数据丢失。因此，在整个DDL过程中，表A中不能有更新。也就是说，这个DDL不是Online的。</p><p>而在<strong>MySQL 5.6版本开始引入的Online DDL，对这个操作流程做了优化。</strong></p><p>我给你简单描述一下引入了Online DDL之后，重建表的流程：</p><ol>
<li>
<p>建立一个临时文件，扫描表A主键的所有数据页；</p>
</li>
<li>
<p>用数据页中表A的记录生成B+树，存储到临时文件中；</p>
</li>
<li>
<p>生成临时文件的过程中，将所有对A的操作记录在一个日志文件（row log）中，对应的是图中state2的状态；</p>
</li>
<li>
<p>临时文件生成后，将日志文件中的操作应用到临时文件，得到一个逻辑数据上与表A相同的数据文件，对应的就是图中state3的状态；</p>
</li>
<li>
<p>用临时文件替换表A的数据文件。</p>
</li>
</ol><p><img src="https://static001.geekbang.org/resource/image/2d/f0/2d1cfbbeb013b851a56390d38b5321f0.png" alt=""></p><center><span class="reference">图4 Online DDL</span></center><p>可以看到，与图3过程的不同之处在于，由于日志文件记录和重放操作这个功能的存在，这个方案在重建表的过程中，允许对表A做增删改操作。这也就是Online DDL名字的来源。</p><p>我记得有同学在第6篇讲表锁的文章<a href="https://time.geekbang.org/column/article/69862">《全局锁和表锁 ：给表加个字段怎么索这么多阻碍？》</a>的评论区留言说，DDL之前是要拿MDL写锁的，这样还能叫Online DDL吗？</p><p>确实，图4的流程中，alter语句在启动的时候需要获取MDL写锁，但是这个写锁在真正拷贝数据之前就退化成读锁了。</p><p>为什么要退化呢？为了实现Online，MDL读锁不会阻塞增删改操作。</p><p>那为什么不干脆直接解锁呢？为了保护自己，禁止其他线程对这个表同时做DDL。</p><p>而对于一个大表来说，Online DDL最耗时的过程就是拷贝数据到临时表的过程，这个步骤的执行期间可以接受增删改操作。所以，相对于整个DDL过程来说，锁的时间非常短。对业务来说，就可以认为是Online的。</p><p>需要补充说明的是，上述的这些重建方法都会扫描原表数据和构建临时文件。对于很大的表来说，这个操作是很消耗IO和CPU资源的。因此，如果是线上服务，你要很小心地控制操作时间。如果想要比较安全的操作的话，我推荐你使用GitHub开源的gh-ost来做。</p><h1>Online 和 inplace</h1><p>说到Online，我还要再和你澄清一下它和另一个跟DDL有关的、容易混淆的概念inplace的区别。</p><p>你可能注意到了，在图3中，我们把表A中的数据导出来的存放位置叫作tmp_table。这是一个临时表，是在server层创建的。</p><p>在图4中，根据表A重建出来的数据是放在“tmp_file”里的，这个临时文件是InnoDB在内部创建出来的。整个DDL过程都在InnoDB内部完成。对于server层来说，没有把数据挪动到临时表，是一个“原地”操作，这就是“inplace”名称的来源。</p><p>所以，我现在问你，如果你有一个1TB的表，现在磁盘间是1.2TB，能不能做一个inplace的DDL呢？</p><p>答案是不能。因为，tmp_file也是要占用临时空间的。</p><p>我们重建表的这个语句alter table t engine=InnoDB，其实隐含的意思是：</p><pre><code>alter table t engine=innodb,ALGORITHM=inplace;
</code></pre><p>跟inplace对应的就是拷贝表的方式了，用法是：</p><pre><code>alter table t engine=innodb,ALGORITHM=copy;
</code></pre><p>当你使用ALGORITHM=copy的时候，表示的是强制拷贝表，对应的流程就是图3的操作过程。</p><p>但我这样说你可能会觉得，inplace跟Online是不是就是一个意思？</p><p>其实不是的，只是在重建表这个逻辑中刚好是这样而已。</p><p>比如，如果我要给InnoDB表的一个字段加全文索引，写法是：</p><pre><code>alter table t add FULLTEXT(field_name);
</code></pre><p>这个过程是inplace的，但会阻塞增删改操作，是非Online的。</p><p>如果说这两个逻辑之间的关系是什么的话，可以概括为：</p><ol>
<li>
<p>DDL过程如果是Online的，就一定是inplace的；</p>
</li>
<li>
<p>反过来未必，也就是说inplace的DDL，有可能不是Online的。截止到MySQL 8.0，添加全文索引（FULLTEXT index）和空间索引(SPATIAL index)就属于这种情况。</p>
</li>
</ol><p>最后，我们再延伸一下。</p><p>在第10篇文章<a href="https://time.geekbang.org/column/article/71173">《MySQL为什么有时候会选错索引》</a>的评论区中，有同学问到使用optimize table、analyze table和alter table这三种方式重建表的区别。这里，我顺便再简单和你解释一下。</p><ul>
<li>从MySQL 5.6版本开始，alter table t engine = InnoDB（也就是recreate）默认的就是上面图4的流程了；</li>
<li>analyze table t 其实不是重建表，只是对表的索引信息做重新统计，没有修改数据，这个过程中加了MDL读锁；</li>
<li>optimize table t 等于recreate+analyze。</li>
</ul><h1>小结</h1><p>今天这篇文章，我和你讨论了数据库中收缩表空间的方法。</p><p>现在你已经知道了，如果要收缩一个表，只是delete掉表里面不用的数据的话，表文件的大小是不会变的，你还要通过alter table命令重建表，才能达到表文件变小的目的。我跟你介绍了重建表的两种实现方式，Online DDL的方式是可以考虑在业务低峰期使用的，而MySQL 5.5及之前的版本，这个命令是会阻塞DML的，这个你需要特别小心。</p><p>最后，又到了我们的课后问题时间。</p><p>假设现在有人碰到了一个“想要收缩表空间，结果适得其反”的情况，看上去是这样的：</p><ol>
<li>
<p>一个表t文件大小为1TB；</p>
</li>
<li>
<p>对这个表执行 alter table t engine=InnoDB；</p>
</li>
<li>
<p>发现执行完成后，空间不仅没变小，还稍微大了一点儿，比如变成了1.01TB。</p>
</li>
</ol><p>你觉得可能是什么原因呢 ？</p><p>你可以把你觉得可能的原因写在留言区里，我会在下一篇文章的末尾把大家描述的合理的原因都列出来，以后其他同学就不用掉到这样的坑里了。感谢你的收听，也欢迎你把这篇文章分享给更多的朋友一起阅读。</p><h1>上期问题时间</h1><p>在上期文章最后，我留给你的问题是，如果一个高配的机器，redo log设置太小，会发生什么情况。</p><p>每次事务提交都要写redo log，如果设置太小，很快就会被写满，也就是下面这个图的状态，这个“环”将很快被写满，write pos一直追着CP。</p><p><img src="https://static001.geekbang.org/resource/image/a2/e5/a25bdbbfc2cfc5d5e20690547fe7f2e5.jpg" alt=""></p><p>这时候系统不得不停止所有更新，去推进checkpoint。</p><p>这时，你看到的现象就是<strong>磁盘压力很小，但是数据库出现间歇性的性能下跌。</strong></p><p>评论区留言点赞板：</p><blockquote>
<p>@某、人 给了一个形象的描述，而且提到了，在这种情况下，连change buffer的优化也失效了。因为checkpoint一直要往前推，这个操作就会触发merge操作，然后又进一步地触发刷脏页操作；<br>
有几个同学提到了内存淘汰脏页，对应的redo log的操作，这个我们会在后面的文章中展开，大家可以先看一下 @melon 同学的描述了解一下；<br>
@算不出流源 提到了“动态平衡”，其实只要出现了这种“平衡”，意味着本应该后台的操作，就已经影响了业务应用，属于有损失的平衡。</p>
</blockquote><p><img src="https://static001.geekbang.org/resource/image/ce/d9/ce7f4e35916ed1aa49206a53a0547bd9.jpg" alt=""></p>
                    </div>
                </div>

            </div>
            <div data-v-87ffcada="" class="article-comments pd"><h2 data-v-87ffcada=""><span
                    data-v-87ffcada="">精选留言</span></h2>
                <ul data-v-87ffcada="">
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/10/4f/78/c3d8ecb0.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">undifined</span>
                            </div>
                            <div class="bd">老师，有几个问题：<br>1.Truncate 会释放表空间吗<br>2.重建表的时候如果没有数据更新，有没有可能产生页分裂和空洞<br>3.页分裂是发生在索引还是数据上<br>4.应用 row log 的过程会不会再次产生页分裂和空洞<br>5.不影响增删改，就是 Online；相对 Server层没有新建临时表，就是 inplace，这里怎么判断是不是相对 Server 层没有新建临时表<br>辛苦老师解答一下，谢谢老师 <br></div>
                            <span class="time">2018-12-12 10:41</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">1. Truncate 可以理解为drop+create<br>2.  Online 可以认为没有<br>3. 数据也是索引哦<br>4. 可能会<br>5. 好问题，我放到明天答疑部分</p>
                                <p class="reply-time">2018-12-12 11:16</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/10/63/70/0b7b5cda.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">郜</span>
                            </div>
                            <div class="bd">麻烦咨询个问题，“在图 3 中，我们把表 A 中的数据导出来的存放位置叫作 tmp_table。这是一个临时表，是在 server 层创建的。”<br>在server层创建的表也是将A表数据copy到了临时表，为什么在空间不够用时就没有问题，而inplace在InnoDB执行则会再占用一份存储？<br> <br></div>
                            <span class="time">2018-12-13 11:07</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">额，<br><br>Copy的时候肯定更要的…<br><br>这里特别指出来，是因为大多数人很容易理解copy需要临时空间，但是误以为inplace不需要<br><br><br>Anyway,好问题 😄</p>
                                <p class="reply-time">2018-12-13 18:13</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/9b/a4/a52a637d.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">陈飞</span>
                            </div>
                            <div class="bd">老师，请问下分布式ID（雪花算法生成的ID）生成的索引会比自增长的ID性能低吗？<br><br>雪花算法生成的ID是越来越大的，但不是逐渐递增，长度用的的bitint，麻烦解答下，非常感谢。 <br></div>
                            <span class="time">2018-12-13 13:57</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">好问题。<br><br><br>性能一样的，没有一定要“连续”，只要是递增</p>
                                <p class="reply-time">2018-12-13 18:11</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/36/d2/c7357723.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">发条橙子 。</span>
                            </div>
                            <div class="bd">老师 ， 我的确实是 5.7 版本。我今天看了些关于 online ddl的文章 ，再结合表锁那章评论的内容，有几个点还想确认一下 ，希望老师能解答一下 。<br><br>1. 是不是 5.6 之后 所有的 alter 操作(增删字段、增删索引等)都是支持 online ddl <br><br>2. 如果 alter 都是 online ddl 那么是不是如果 alter操作 获取到mdl写锁 时， 后面的 查询需要mdl读锁会暂时阻塞， 但是mdl会马上降为读锁 ，后面的操作会继续进行不会堵塞 。等再升到写锁 ，后面操作又会暂时阻塞。 <br><br>3. 当 alter 降到mdl 读锁时 ， 这时候可以新增数据么 ， mdl表级读锁 不会影响到 insert 或者 update的行锁么 <br><br>4. 如果将 alter 操作显式的放到事务里 ，事务不提交 ， 另一个事务查询的时候会查询到alter 操作后的表结构 ， 比如新增了一个字段。这个是什么原因 ，是否打破了 mvcc 的定义呢 <br></div>
                            <span class="time">2018-12-13 23:49</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">1. 不是哦，我文章里说的加全文索引就不online<br><br>2. 对，这两个暂时，都是时间很短的<br><br>3. 是，DML语句加的是MDL读锁，读读不冲突<br><br>4. 好问题 ， 不过alter table 语句会默认提交前面的事务，然后自己独立执行😄</p>
                                <p class="reply-time">2018-12-14 00:36</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="http://thirdwx.qlogo.cn/mmopen/vi_32/mcibngvIYnyDbzWPrWu0hD6WvZzjuiawXTSeLQibMJxa7gMtL7RrmT5Qia0esMJSlSLAKC5TC5JpHquKgBNgE8Uhrg/132" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">飞翔</span>
                            </div>
                            <div class="bd">我想到的其中一种可能：<br>本来就很紧凑，没能整出多少剩余空间。<br>重新收缩的过程中，页会按90%满的比例来重新整理页数据（10%留给UPDATE使用），<br>未整理之前页已经占用90%以上，收缩之后，文件就反而变大了。 <br></div>
                            <span class="time">2018-12-12 11:16</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">一个漂亮的答案</p>
                                <p class="reply-time">2018-12-12 11:43</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/e8/45/c58cb283.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">帆帆帆帆帆帆帆帆</span>
                            </div>
                            <div class="bd">@undifined怎么判断是不是相对 Server 层没有新建临时表。一个最直观的判断方法是看命令执行后影响的行数，没有新建临时表的话新建的行数是0。 <br></div>
                            <span class="time">2018-12-12 14:41</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">👍🏿，下一篇答疑直接贴你答案😄</p>
                                <p class="reply-time">2018-12-12 17:56</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/f8/70/f3a33a14.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">某、人</span>
                            </div>
                            <div class="bd">通过第10期的课后思考题学习到如果delete的数据还会被用于MVCC,那么该数据页(二级索引和聚簇索引)上的记录不会被物理删除,是被标记删除。只有当该事务不会用于mvcc了,才可以被purge线程把之前标记删除的数据真正删除掉.但是即便数据物理删除了,磁盘空间也不会返回给操作系统.可以通过show table status like &#39;t&#39;;观察data_free来预估该表的碎片。如果过大,可以用alter table t engine=innodb来清除<br>我有几个问题请教下老师:<br>1.inplace相对于其他在线改表软件,多了MDL X锁.既然都是通过临时表&#47;文件来做,为什么一开始要上MDL X锁？<br>2.gh-ost使用binlog来做同步,假设从position 1开始,先lock S前面1000条数据做cp,这时有事务对后面1000条数据做了修改或者插入。等cp后面这个1000条时,会把修改好的数据cp到临时表.最后又应用binlog,那么这相当于做了两次操作,请问这部分数据是怎么处理的？<br>3.online会把过程中对表的操作记录在一个(row log)中,那么中途这些DML事务,是怎么判定的commit?我做测试,中途这些事务都是成功的。但是有在做online DDL快完了,commit那个阶段,DDL报唯一键冲突,这又是什么原因造成的啊？我没有模拟出来这个例子 <br></div>
                            <span class="time">2018-12-13 20:26</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">1. 确保没有启发请求在用这个表<br><br>2. Binlog设置为row格式，幂等的<br><br>3. 这个我觉得其实是bug,就没提。你在DDL期间，往原表插入一个已经存在相同主键的一行试试</p>
                                <p class="reply-time">2018-12-13 21:51</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/07/1e/bdbe93f4.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">尘封</span>
                            </div>
                            <div class="bd">老师，请教一个问题，和本文无关，既然mysql支持了打包数据排序模式，能够更紧凑的分配内存进行排序，那定义表结构的时候，varchar(10)存储hello和varchar(100)存储hello的优势在哪里呢？谢谢 <br></div>
                            <span class="time">2018-12-12 07:32</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">你这个问题放在第16篇问就刚刚好了。<br><br>以前不支持紧凑排序的时候有，现在没啥了差别了，小于256都差不多</p>
                                <p class="reply-time">2018-12-12 10:47</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/11/a2/97c0de02.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">V。</span>
                            </div>
                            <div class="bd">可能性有两个。<br>1. 生成临时表过程中，row log中新增的数据较多，导致表的行数变多。<br>2. 生成临时表过程中，row log中有部分插入和删除操作会产生“空洞”。<br>个人感觉，第一种情况，突然多了高于1％的数据，比较少见；但第二种情况，在某些业务中，经常出现。 <br></div>
                            <span class="time">2018-12-12 09:46</span>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">库淘淘</span>
                            </div>
                            <div class="bd">alter table A engine=InnoDB 中由写锁降为读锁。有个疑问 为何不直接就加个MDL读锁 ,这样DDL 也执行不了,应用redo 替换文件后释放读锁即可 <br></div>
                            <span class="time">2018-12-13 16:04</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">如果有别的线程正在读这个表的数据，得等下</p>
                                <p class="reply-time">2018-12-13 18:07</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/df/e7/e3c450c2.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">lionetes</span>
                            </div>
                            <div class="bd">在alter table engine =innodb  未完成前,做了DML 操作, 并且是在已经重构完的数据引起的DML操作，产生了空洞. 导致变大 重构未处理的数据 即使产生了 空洞，到100% 后也能清理碎片空间.<br><br>truncate table  里面的 数据 不产生 binlog,无法恢复， 如果速度上面来讲 还是drop table 更快.  但对于 超大的table 做drop， truncate 时候也是很危险了，容易整个数据库 hang 住，因为要对 buffer pool 中对应的页面清除, 可以对ibd 文件做 ln 操作生成 ibd.hdlk  然后 rm -f  ibd 最后 drop table 会很快<br><br>对经常delete 数据的表 用以前讲的 循环删除 方法，来删除 也是 不错的. 当然要根据情况做 碎片整理 <br></div>
                            <span class="time">2018-12-12 22:34</span>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/e8/45/c58cb283.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">帆帆帆帆帆帆帆帆</span>
                            </div>
                            <div class="bd">执行完成后，空间不仅没变小，还稍微大了一点儿。比较常见的情况是表上只有insert，没有update的情况，如一些存放历史操作记录的表。 <br></div>
                            <span class="time">2018-12-12 14:42</span>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/10/41/f7/277f14bb.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">wang chen wen</span>
                            </div>
                            <div class="bd">optimize table t 等于 recreate+analyze<br>老师请教个问题recreate出来应该是几乎全新的，analyze的必要性？ <br></div>
                            <span class="time">2018-12-12 09:45</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">好问题，这个得是比较极端的情况下才有必要，所以我比较喜欢直接用alter </p>
                                <p class="reply-time">2018-12-12 10:30</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/12/e9/29/629d9bb0.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">天王</span>
                            </div>
                            <div class="bd"> delete删除表数据，只是打上一个可复用标记，如果是数据页上一部分数据打上标记，如果按照自增主键insert数据，那表空间的数据不会复用，如果是整个数据页的所有数据打上标记，那么可以复用。2个3相临的数据页，如果空洞太多，合并成一个页，另外一个可以标记复用。重建表可以减少空洞，文件大小可以减小，本质上是通过，创建临时文件，将数据在临时文件上，重建一份，重建过程，按照顺序插入，极大减少了黑洞，数据都拷贝到临时文件以后，会有删除原来文件，切换到新文件。文件会减小了。<br><br><br> <br></div>
                            <span class="time">2018-12-12 09:14</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">👍🏿<br></p>
                                <p class="reply-time">2018-12-12 10:32</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/cb/50/66d0bd7f.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">杰之7</span>
                            </div>
                            <div class="bd">通过这一节的阅读学习，了解了删除表中的数据以后，表的大小并没有减少，这种现象我们叫做空洞，对于增删改都会产生这种现象。为什么会是这样，我想是计算机系统或许内存方面的内容。<br><br>对于这种现象，老师给予的解决方法是重建表，就是将原表数据重新建立在一张表。同时在高版本Mysql的Innodb支持online操作，同时，老师解释了onplace和inplace的区别。<br><br>中途有两个礼拜没有跟上进度学习，很惭愧，接下来一月会尽快跟上老师的步伐。 <br></div>
                            <span class="time">2019-01-10 13:21</span>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/12/be/d4/ff1c1319.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">金龟</span>
                            </div>
                            <div class="bd">老师，请教个问题，在innodb中是不是b+树的每个节点都是一页，比如最上层的根节点就是一页。下层有5个节点就是5页。 <br></div>
                            <span class="time">2018-12-30 11:45</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">是的</p>
                                <p class="reply-time">2018-12-30 19:31</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/e3/4b/5046906a.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">海曙云崖</span>
                            </div>
                            <div class="bd">怎么判断online DDL 的阶段是在MDL还是读锁阶段呢 <br></div>
                            <span class="time">2018-12-26 15:10</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">Show processlist可以看到<br><br>不过MDL锁阶段很短</p>
                                <p class="reply-time">2018-12-26 17:06</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/10/ed/d2/e3ae7ddd.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">三木禾</span>
                            </div>
                            <div class="bd">数据页A满了，随机插入一条数据，不得不申请一个新的数据页，这时候数据页A会留下空洞，我的问题是，既然满了，为什么还有空洞呢？ <br></div>
                            <span class="time">2018-12-24 15:18</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">申请新页B，把一部分数据从A挪到B，A就空出一些</p>
                                <p class="reply-time">2018-12-24 16:20</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://static001.geekbang.org/account/avatar/00/13/e0/25/d87b1c9b.jpg" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">zzkkll99</span>
                            </div>
                            <div class="bd">老师我想问一下，为什么对表结构进行修改要读原数据。不可以直接修改吗 <br></div>
                            <span class="time">2018-12-20 17:28</span>
                            
                            <div class="reply">
                                <div class="reply-hd"><span>作者回复</span></div>
                                <p class="reply-content">你说的原数据是说表里的数据吗？<br><br>MySQL 官方版本还不支持只改表结构😓</p>
                                <p class="reply-time">2018-12-20 18:07</p>
                            </div>
                            
                        </div>
                    </li>
                    
                    <li data-v-87ffcada="" class="comment-item"><img
                            src="https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKVIjh4T1akib7wAQXiaMmTRVe89bT3tBAeSdFflCQSWjDZQOs5jeBEWLC1GyFshHjvhsIZKArO6ichw/132" class="avatar">
                        <div class="info">
                            <div class="hd"><span class="username">壹口尘埃</span>
                            </div>
                            <div class="bd">网上找到一个Inplace和Online的区别，写的挺好的，贴出来方便大家理解：<br>MySQL各版本，对于add Index的处理方式是不同的，主要有三种：<br><br>（1）Copy Table方式<br>这是InnoDB最早支持的创建索引的方式。顾名思义，创建索引是通过临时表拷贝的方式实现的。<br><br>新建一个带有新索引的临时表，将原表数据全部拷贝到临时表，然后Rename，完成创建索引的操作。<br><br>这个方式创建索引，创建过程中，原表是可读的。但是会消耗一倍的存储空间。<br><br>（2）Inplace方式<br>这是原生MySQL 5.5，以及innodb_plugin中提供的创建索引的方式。所谓Inplace，也就是索引创建在原表上直接进行，不会拷贝临时表。相对于Copy Table方式，这是一个进步。<br><br>Inplace方式创建索引，创建过程中，原表同样可读的，但是不可写。<br><br>（3）Online方式<br>这是MySQL 5.6.7中提供的创建索引的方式。无论是Copy Table方式，还是Inplace方式，创建索引的过程中，原表只能允许读取，不可写。对应用有较大的限制，因此MySQL最新版本中，InnoDB支持了所谓的Online方式创建索引。<br><br>InnoDB的Online Add Index，首先是Inplace方式创建索引，无需使用临时表。在遍历聚簇索引，收集记录并插入到新索引的过程中，原表记录可修改。而修改的记录保存在Row Log中。当聚簇索引遍历完毕，并全部插入到新索引之后，重放Row Log中的记录修改，使得新索引与聚簇索引记录达到一致状态。<br><br>与Copy Table方式相比，Online Add Index采用的是Inplace方式，无需Copy Table，减少了空间开销；与此同时，Online Add Index只有在重放Row Log最后一个Block时锁表，减少了锁表的时间。<br><br>与Inplace方式相比，Online Add Index吸收了Inplace方式的优势，却减少了锁表的时间。 <br></div>
                            <span class="time">2018-12-19 14:40</span>
                            
                        </div>
                    </li>
                    


                </ul>
            </div>
        </div>
    </div>
</div>
</body>
</html>